package com.lxw.manage_cms.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lxw.framework.domain.cms.CmsPage;
import com.lxw.framework.domain.cms.CmsTemplate;
import com.lxw.framework.domain.cms.request.QueryPageRequest;
import com.lxw.framework.domain.cms.response.CmsCode;
import com.lxw.framework.domain.cms.response.CmsPageResult;
import com.lxw.framework.exception.BizException;
import com.lxw.framework.model.response.CommonCode;
import com.lxw.manage_cms.config.RabbitMQConfig;
import com.lxw.manage_cms.dao.CmsPageDao;
import com.lxw.manage_cms.dao.CmsPreviewDao;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class CmsPageService {
    @Autowired
    CmsPageDao cmsPageDao;
    @Autowired
    CmsPreviewDao cmsPreviewDao;
    @Autowired
    GridFsTemplate gridFsTemplate;
    @Autowired
    GridFSBucket gridFSBucket;
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 分页查询
     *
     * @param page             页码 方法内部做了 -= 1 处理
     * @param size             每页条数
     * @param queryPageRequest 查询条件包装类  pageAliase 别名 为模糊查询
     * @return
     */

    public Page<CmsPage> findList(Integer page, Integer size, QueryPageRequest queryPageRequest) {
        //对分页数据进行处理
        if (page <= 0) {
            page = 1;
        }
        page = page - 1;//mongoDB分页PageRequest 页面是从0开始的
        if (size <= 0) {
            size = 10; //每页条数小于等于0 默认每页条数为10
        }
        //分页对象
        Pageable pageable = PageRequest.of(page, size);

        CmsPage cmsPage = new CmsPage();
        //条件查询 QueryPageRequest查询条件包装类
        //判断站点id
        if (StringUtils.isNotEmpty(queryPageRequest.getSiteId())) {
            cmsPage.setSiteId(queryPageRequest.getSiteId());
        }
        //判断页面id
        if (StringUtils.isNotEmpty(queryPageRequest.getPageId())) {
            cmsPage.setPageId(queryPageRequest.getPageId());
        }
        //页面名称
        if (StringUtils.isNotEmpty(queryPageRequest.getPageName())) {
            cmsPage.setPageName(queryPageRequest.getPageName());
        }
        //页面类型  0/1 => 静态/动态
        if (StringUtils.isNotEmpty(queryPageRequest.getPageType())) {
            cmsPage.setPageType(queryPageRequest.getPageType());
        }
        //别名
        if (StringUtils.isNotEmpty(queryPageRequest.getPageAliase())) {
            cmsPage.setPageAliase(queryPageRequest.getPageAliase());
        }
        //模板id
        if (StringUtils.isNotEmpty(queryPageRequest.getTemplateId())) {
            cmsPage.setTemplateId(queryPageRequest.getTemplateId());
        }


        //根据条件查询  别名为模糊查询
        //ExampleMatcher.matching().withMatcher("pageAliase", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.DEFAULT));
        ExampleMatcher exampleMatcher = ExampleMatcher.matching().withMatcher("pageAliase", ExampleMatcher.GenericPropertyMatchers.contains());
        Example<CmsPage> of = Example.of(cmsPage, exampleMatcher);

        //分页 模糊 联合查询
        Page<CmsPage> all = cmsPageDao.findAll(of, pageable);


        //返回结果
        return all;
    }

    /**
     * cmsPage 数据添加
     *
     * @param cmsPage
     * @return
     */
    public CmsPage add(CmsPage cmsPage) {
        //判断非空
        if (StringUtils.isEmpty(cmsPage.getPageName())) {
            //throw new RuntimeException("页面名称不能为空");
            throw new BizException(CommonCode.FAIL);

        }
        if (StringUtils.isEmpty(cmsPage.getSiteId())) {
            //throw new RuntimeException("站点id不能为空");
            throw new BizException(CommonCode.FAIL);
        }
        //TODO 其他非空校验

        //页面名称、站点Id、页面webpath为唯一索引
        CmsPage cmsPage1 = new CmsPage();
        cmsPage1.setPageName(cmsPage.getPageName());
        cmsPage1.setSiteId(cmsPage.getSiteId());
        cmsPage1.setPageWebPath(cmsPage.getPageWebPath());
        Example<CmsPage> of = Example.of(cmsPage1);
        List<CmsPage> all = cmsPageDao.findAll(of);
        if (all != null && all.size() > 0) {
            //throw new RuntimeException("页面名称、站点Id、页面webpath不是唯一索引");
            throw new BizException(CmsCode.CMS_ADDPAGE_NOTONE);

        }
        CmsPage insert = cmsPageDao.insert(cmsPage);
        return insert;
    }

    /**
     * 根据id查询
     *
     * @param id
     * @return
     */
    public CmsPageResult findById(String id) {
        Optional<CmsPage> byId = cmsPageDao.findById(id);
        if (byId.isPresent()) {
            return new CmsPageResult(CommonCode.SUCCESS, byId.get());
        } else {
            return new CmsPageResult(CommonCode.SERVER_ERROR, null);
        }
    }

    public CmsPageResult edit(String id, CmsPage cmsPage) {
        //    根据id查询
        Optional<CmsPage> byId = cmsPageDao.findById(id);
        if (!byId.isPresent()) {
            throw new RuntimeException("id非法，查询不到值");
        }
        CmsPage page = byId.get();

        //    非空校验
        //判断站点id
        if (StringUtils.isNotEmpty(cmsPage.getSiteId())) {
            page.setSiteId(cmsPage.getSiteId());
        }
        //模板id
        if (StringUtils.isNotEmpty(cmsPage.getTemplateId())) {
            page.setTemplateId(cmsPage.getTemplateId());
        }
        //页面名称
        if (StringUtils.isNotEmpty(cmsPage.getPageName())) {
            page.setPageName(cmsPage.getPageName());
        }
        //别名
        if (StringUtils.isNotEmpty(cmsPage.getPageAliase())) {
            page.setPageAliase(cmsPage.getPageAliase());
        }
        //访问路径
        if (StringUtils.isNotEmpty(cmsPage.getPageWebPath())) {
            page.setPageWebPath(cmsPage.getPageWebPath());
        }
        //物理路径 pagePhysicalPath
        if (StringUtils.isNotEmpty(cmsPage.getPagePhysicalPath())) {
            page.setPagePhysicalPath(cmsPage.getPagePhysicalPath());
        }
        //    类型 pageType
        if (StringUtils.isNotEmpty(cmsPage.getPageType())) {
            page.setPageType(cmsPage.getPageType());
        }
        //数据url
        if (StringUtils.isNotEmpty(cmsPage.getDataUrl())) {
            page.setDataUrl(cmsPage.getDataUrl());
        }
        cmsPageDao.save(page);
        return new CmsPageResult(CommonCode.SUCCESS, page);

    }

    public CmsPageResult deleteById(String id) {

        if (StringUtils.isNotEmpty(id)) {
            try {
                cmsPageDao.deleteById(id);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return new CmsPageResult(CommonCode.SUCCESS, null);
        } else {
            throw new RuntimeException("id不合法");
        }
    }


    public String preview(String id) {
        //通过id获取cmsPage
        CmsPageResult cmsPageResult = findById(id);
        CmsPage cmsPage = cmsPageResult.getCmsPage();
        //获取模板id templateId
        String templateId = cmsPage.getTemplateId();
        //通过模板id 获取模板信息CmsTemplate
        CmsTemplate cmsTemplate = getCmsTemplateById(templateId);
        //获取模板文件id templateFileId
        String templateFileId = cmsTemplate.getTemplateFileId();
        //资源文件合并流
        GridFsResource gridFsResource = getTemplateFile(templateFileId);
        String templateStr = null;
        try {
            templateStr = IOUtils.toString(gridFsResource.getInputStream(), "UTF-8");
        } catch (IOException e) {
            throw new BizException(CmsCode.CMS_COURSE_PERVIEWISNULL);
        }

        //获取模型数据
        String dataUrl = cmsPage.getDataUrl();
        if (StringUtils.isEmpty(dataUrl)) {
            throw new BizException(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }
        String forObject = restTemplate.getForObject(dataUrl, String.class);
        JSONObject jsonObject = JSON.parseObject(forObject);
        JSONArray jsonArray = jsonObject.getJSONArray("model");


        //静态化
       /*
       // 第一步：创建一个Configuration对象，直接new一个对象。构造方法的参数就是freemarker对于的版本号。

        Configuration configuration = new Configuration(Configuration.getVersion());

        // 第二步：设置模板文件所在的路径。

        configuration.setDirectoryForTemplateLoading(new File("D:/workspaces-itcast/term197/e3-item-web/src/main/webapp/WEB-INF/ftl"));

        // 第三步：设置模板文件使用的字符集。一般就是utf-8.

        configuration.setDefaultEncoding("utf-8");

        // 第四步：加载一个模板，创建一个模板对象。

        Template template = configuration.getTemplate("hello.ftl");

        // 第五步：创建一个模板使用的数据集，可以是pojo也可以是map。一般是Map。

        Map dataModel = new HashMap<>();

        //向数据集中添加数据

        dataModel.put("hello", "this is my first freemarker test.");

        // 第六步：创建一个Writer对象，一般创建一FileWriter对象，指定生成的文件名。

        Writer out = new FileWriter(new File("D:/temp/term197/out/hello.html"));

        // 第七步：调用模板对象的process方法输出文件。

        template.process(dataModel, out);

        // 第八步：关闭流。

        out.close();
        */
        String html = null;
        try {
            html = getHeml(templateStr, jsonArray);
        } catch (IOException e) {
            e.printStackTrace();
            throw new BizException(CommonCode.FAIL);
        } catch (TemplateException e) {
            e.printStackTrace();
            throw new BizException(CmsCode.CMS_GENERATEHTML_HTMLISNULL);
        }
        return html;
    }

    /**
     * 获取生成的html字符串
     *
     * @param templateStr
     * @param jsonArray
     * @throws IOException
     * @throws TemplateException
     */
    public String getHeml(String templateStr, JSONArray jsonArray) throws IOException, TemplateException {
        //模板文件在数据库中 获取字符串，从字符串中创建模板
        // 1、创建一个Configuration对象，直接new一个对象。构造方法的参数就是freemarker对于的版本号。
        Configuration configuration = new Configuration(Configuration.getVersion());

        //2、创建一个stringTemplateLoader对象，加载模板字符串
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        //生成系统时间字符串作为模板文件名 也可以使用md5
        String templateHtmlName = String.valueOf(System.currentTimeMillis());
        stringTemplateLoader.putTemplate(templateHtmlName, templateStr);
        configuration.setTemplateLoader(stringTemplateLoader);
        //设置模板文件使用的字符集。一般就是utf-8.
        configuration.setDefaultEncoding("utf-8");

        //3、加载一个模板，创建一个模板对象。
        Template template = configuration.getTemplate(templateHtmlName);
        //4、创建一个模板使用的数据集，可以是pojo也可以是map。一般是Map。

        Map<String, Object> dataModel = new HashMap<>();

        //向数据集中添加数据
        dataModel.put("model", jsonArray);
        //5、生成
        String res = FreeMarkerTemplateUtils.processTemplateIntoString(template, dataModel);
        return res;
    }

    /**
     * 通过templateFileId 获取文件合并流  模板文件+元数据
     *
     * @param templateFileId
     * @return GridFsResource
     */
    public GridFsResource getTemplateFile(String templateFileId) {
        //查询出模板文件
        Criteria criteria = Criteria.where("_id").is(templateFileId);
        GridFSFile one = gridFsTemplate.findOne(Query.query(criteria));

        //获取文件流
        GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(one.getObjectId());
        // 3. 资源操作对象获取文件合并流
        GridFsResource gridFsResource = new GridFsResource(one, gridFSDownloadStream);
        return gridFsResource;
    }

    /**
     * 通过模板id 获取模板信息CmsTemplate
     *
     * @param templateId
     * @return CmsTemplate
     */
    public CmsTemplate getCmsTemplateById(String templateId) {

        Optional<CmsTemplate> byId = cmsPreviewDao.findById(templateId);
        if (byId.isPresent()) {
            return byId.get();
        } else {
            throw new BizException(CmsCode.CMS_COURSE_PERVIEWISNULL);
        }
    }

    public CmsPageResult pagePost(String id) {
        //通过id获取cmsPage
        CmsPageResult cmsPageResult = findById(id);
        CmsPage cmsPage = cmsPageResult.getCmsPage();
        //获取模板id templateId
        String templateId = cmsPage.getTemplateId();
        //通过模板id 获取模板信息CmsTemplate
        CmsTemplate cmsTemplate = getCmsTemplateById(templateId);
        //获取模板文件id templateFileId
        String templateFileId = cmsTemplate.getTemplateFileId();
        //资源文件合并流
        GridFsResource gridFsResource = getTemplateFile(templateFileId);
        String templateStr = null;
        try {
            templateStr = IOUtils.toString(gridFsResource.getInputStream(), "UTF-8");
        } catch (IOException e) {
            throw new BizException(CmsCode.CMS_COURSE_PERVIEWISNULL);
        }

        //获取模型数据
        String dataUrl = cmsPage.getDataUrl();
        if (StringUtils.isEmpty(dataUrl)) {
            throw new BizException(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }
        String forObject = restTemplate.getForObject(dataUrl, String.class);
        JSONObject jsonObject = JSON.parseObject(forObject);
        JSONArray jsonArray = jsonObject.getJSONArray("model");
        //静态化
        String html = null;
        try {
            html = getHeml(templateStr, jsonArray);
            //
            ObjectId store = gridFsTemplate.store(IOUtils.toInputStream(html,"utf-8"),cmsPage.getPageName());
            cmsPage.setHtmlFileId(store.toString());
            cmsPageDao.save(cmsPage);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BizException(CommonCode.FAIL);
        }
        //通过MQ写入磁盘
        rabbitTemplate.convertAndSend(RabbitMQConfig.EX_ROUTING_CMS_POSTPAGE,"5a751fab6abb5044e0d19ea1",id);
        return new CmsPageResult(CommonCode.SUCCESS, null);
    }
}
